package com.duan.nowcoder.community.util;

import org.apache.commons.lang3.CharUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

@Component
public class SensitiveWordsFilter {
    private static final Logger logger = LoggerFactory.getLogger(SensitiveWordsFilter.class);

    private final String REPLACEMENT = "***";

    private TireNode tireNode = new TireNode();

    @PostConstruct
    public void init() {
        try (   //将文件转化为字节流
                InputStream is = this.getClass().getClassLoader().getResourceAsStream("sensitive_words.txt");
                //将字节流转化为缓冲流
                BufferedReader reader = new BufferedReader(new InputStreamReader(is))
        ) {
            String keyword;
            while ((keyword = reader.readLine()) != null) {
                this.addKeywords(keyword);
            }
        } catch (Exception e) {
            logger.error("服务器加载敏感词失败" + e.getMessage());
        }
    }

    //将敏感词添加到前缀树
    private void addKeywords(String keyword) {
        //指向root
        TireNode cur = tireNode;
        for (int i = 0; i < keyword.length(); i++) {
            //子节点中没有该字符
            if (cur.getSonNode(keyword.charAt(i)) == null) {
                //新建节点插入
                cur.addSonNode(keyword.charAt(i), new TireNode());
            }
            //指向下一个节点
            cur = cur.getSonNode(keyword.charAt(i));
            //设置标志位
            if (i == keyword.length() - 1) cur.setKeywordsEnd(true);
        }
    }

    /**
     * 过滤敏感词
     *
     * @param text 待过滤的敏感词
     * @return *****
     */
    public String filter(String text) {
        if (text == null) return "";
        int main = 0, ass = 0;
        TireNode cur = tireNode;
        StringBuilder builder = new StringBuilder();
        while (ass < text.length()) {
            char c = text.charAt(ass);
            if (isSymbol(c)) {
                //如果主指针处于根节点，将此符号计入结果，让副指针继续走
                if (cur == tireNode) {
                    builder.append(c);
                    main++;
                }
                //副指针只要碰到符号就后移，主指针只有未开始匹配（树指针为根节点）时碰到符号才后移
                ass++;
                continue;
            }
            //寻找当前副指针指向的字符是否在树里
            cur = cur.getSonNode(c);
            //如果不在将主指针指向的字符压入builder，主指针向后移，树指针指向根节点
            if (cur == null) {
                builder.append(text.charAt(main));
                ass = ++main;
                cur = tireNode;
            } else if (cur.isKeywordsEnd()) {
                //如果副指针指向标志结束处，将代替符号压入builder，主副指针移至副指针后一个
                builder.append(REPLACEMENT);
                main = ++ass;
                cur = tireNode;
            } else {
                //否则副指针后移继续检查
                ass++;
            }
        }
        //将最后一批字符压入
        builder.append(text.substring(main));
        return builder.toString();
    }

    //判断是否为特殊符号
    private boolean isSymbol(Character c) {
        //0x2E80~0x9FFF是东亚文字
        //CharUtils.isAsciiAlphanumeric判断是否为特殊符号
        return !CharUtils.isAsciiAlphanumeric(c) && (c < 0x2E80 || c > 0x9FFF);
    }

    //前缀树
    private class TireNode {
        //关键词结束标志
        private boolean isKeywordsEnd = false;
        //key是下级字符，value是下级节点（root为null）
        private Map<Character, TireNode> sonNode = new HashMap<>();

        public boolean isKeywordsEnd() {
            return isKeywordsEnd;
        }

        public void setKeywordsEnd(boolean keywordsEnd) {
            isKeywordsEnd = keywordsEnd;
        }

        public void addSonNode(Character val, TireNode son) {
            sonNode.put(val, son);
        }

        public TireNode getSonNode(Character val) {
            return sonNode.get(val);
        }
    }
}
